package http;

import frame.service.context.ServiceContext;
import frame.service.standard.Constants;
import frame.service.standard.CustomConfig;
import frame.service.standard.MiayRuntimeException;
import frame.service.standard.ServiceExec;
import org.apache.tomcat.util.http.fileupload.ByteArrayOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.view.AbstractView;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

@RestController
public class Gateway {
    private CustomConfig customConfig;
    private ServiceExec serviceExec;
    private MiayViewResolver viewResolver;

    /**
     * 自定义请求处理
     * */
    @Autowired(required = false)
    public void setCustomConfig(CustomConfig customConfig) {
        this.customConfig = customConfig;
    }

    /**
     * 服务调度配置
     * */
    @Autowired
    public void setServiceExec(ServiceExec serviceExec) {
        this.serviceExec = serviceExec;
    }

    /**
     * 响应解析
     * */
    @Autowired
    public void setViewResolver(MiayViewResolver viewResolver) {
        this.viewResolver = viewResolver;
    }

    //根据Content-Type转换请求报文器
    private static Map<String, HttpMessageConverter> httpMessageConverterMap = new HashMap<>();
    //根据Content-Type转换请求报文转换所需结果
    private static Map<String, Class> httpMessageConverterResClass = new HashMap<>();

    private Logger logger = LoggerFactory.getLogger(Gateway.class);

    static{
        /**默认*/
        httpMessageConverterMap.put(Constants.DEFAULT_SERVICE_HANDLE, new ByteArrayHttpMessageConverter());
        /**text/plain*/
        httpMessageConverterMap.put(MediaType.TEXT_PLAIN_VALUE, new StringHttpMessageConverter());
        /**application/x-www-form-urlencoded*/
        httpMessageConverterMap.put(MediaType.APPLICATION_FORM_URLENCODED_VALUE, new FormHttpMessageConverter());
        /**application/json*/
        httpMessageConverterMap.put(MediaType.APPLICATION_JSON_VALUE, new MappingJackson2HttpMessageConverter());
        /**application/xml*/
        httpMessageConverterMap.put(MediaType.APPLICATION_XML_VALUE, new MappingJackson2XmlHttpMessageConverter());

        httpMessageConverterResClass.put(Constants.DEFAULT_SERVICE_HANDLE, byte[].class);
        httpMessageConverterResClass.put(MediaType.TEXT_PLAIN_VALUE, String.class);
        httpMessageConverterResClass.put(MediaType.APPLICATION_FORM_URLENCODED_VALUE, MultiValueMap.class);
        httpMessageConverterResClass.put(MediaType.APPLICATION_JSON_VALUE, Map.class);
        httpMessageConverterResClass.put(MediaType.APPLICATION_XML_VALUE, Map.class);
    }

    /**
     * 1.${application.name} 通过Java虚拟机参数设置 -Dapplication.name=test 或者 java --application.name=test 程序参数设置
     * 2.safeMode 安全模式 module 应用模块 server 服务 action 操作 version 版本
     * */
    @RequestMapping(path = "/${application.name}/{safeMode}/{module}/{service}/{action}-{version}")
    public void gw(
            @PathVariable String safeMode,
            @PathVariable String module,
            @PathVariable String service,
            @PathVariable String version,
            @PathVariable String action,
            @RequestHeader HttpHeaders headers,
            HttpServletRequest servletRequest,
            HttpServletResponse servletResponse
    ){
        Map<String, Object> reqData = new HashMap<>();
        HttpInputMessage message = createHttpInputMessage(servletRequest, headers, reqData);
        ServiceContext serviceContext = new ServiceContext();

        //解析request数据，设置到上下文context内
        String contentType = null;
        if(customConfig!=null && customConfig.checkCustomRequest(safeMode, module, service, action)){
            contentType = Constants.DEFAULT_SERVICE_HANDLE;
        }else{
            if(headers.getContentType()!=null){
                contentType = headers.getContentType().getType()+"/"+headers.getContentType().getSubtype();
            }else{
                contentType = Constants.DEFAULT_SERVICE_HANDLE;
            }
        }
        serviceContext.setContentType(contentType);

        try {
            HttpMessageConverter httpMessageConverter = null;
            httpMessageConverter = httpMessageConverterMap.get(contentType);

            if(message.getBody().available()>0){
                Object res = httpMessageConverter.read(httpMessageConverterResClass.get(contentType), message);
                if(res instanceof Map){
                    reqData.putAll((Map)res);
                }
                //请求源数据
                reqData.put(Constants.REQUEST_MSG_CONTENT, res);
            }

            reqData.put(Constants.REQUEST_SAFE_MODE, safeMode);
            reqData.put(Constants.REQUEST_MODULE, module);
            reqData.put(Constants.REQUEST_SERVICE, service);
            reqData.put(Constants.REQUEST_ACTION, action);
            reqData.put(Constants.REQUEST_VERSION, version);
            if(logger.isDebugEnabled()){
                logger.debug(reqData.toString());
            }
            serviceContext.setReqData(reqData);
        } catch (Exception e) {
            throw createCustomException(e, serviceContext);
        }

        //服务处理
        try{
            service(serviceContext);
        }catch (Exception e){
            throw createCustomException(e, serviceContext);
        }

        //生成响应
        AbstractView view = null;
        try {
            view = viewResolver.resolveViewName(contentType, null);
        } catch (Exception e) {
            throw createCustomException(e, serviceContext);
        }

        try {
            Map<String, Object> model = viewResolver.createModel(contentType, serviceContext.getRespData(), serviceContext.getStatusCode(), serviceContext.getRespMsg(), serviceContext);
            view.render(model, servletRequest, servletResponse);
        } catch (Exception e) {
            throw createCustomException(e, serviceContext);
        }
    }

    //服务受理
    private void service(ServiceContext serviceContext) {
        if(serviceExec==null) {
            logger.error("910001");
            throw new MiayRuntimeException("910001");
        }
        serviceExec.serviceInvoke(serviceContext);
    }


    /**
     * 创建转换消息
     * 1.创建HttpInputMessage
     * 2.解析请求数据至ServiceContext的ReqData内
     * */
    private HttpInputMessage createHttpInputMessage(HttpServletRequest servletRequest, HttpHeaders headers, Map<String, Object> reqData){
        byte[] request = null;
        try {
            ByteArrayOutputStream bout = new ByteArrayOutputStream();
            byte[] buf = new byte[1024];
            ServletInputStream inputStream = servletRequest.getInputStream();
            int read = -1;
            while((read = inputStream.read(buf))!=-1){
                bout.write(buf, 0, read);
            }
            inputStream.close();
            request = bout.toByteArray();
        } catch (IOException exception) {
            throw new MiayRuntimeException(exception);
        }
        final byte[] body = request;

        //获取请求源数据字节数组
        reqData.put(Constants.REQUEST_BODY_BYTE, request);
        //源请求http头
        reqData.put(Constants.REQUEST_HEADERS, headers);
        //源请求QueryString
        reqData.put(Constants.REQUEST_QUERY_STRING, servletRequest.getQueryString());
        //源请求方法
        reqData.put(Constants.REQUEST_HTTP_METHOD, servletRequest.getMethod().toLowerCase());

        Enumeration<String> attributeNames = servletRequest.getAttributeNames();
        Map<String, Object> requestAttr = new HashMap<>(4);
        while(attributeNames.hasMoreElements()){
            String attr = attributeNames.nextElement();
            requestAttr.put(attr, servletRequest.getAttribute(attr));
        }
        //servletRequest attributes
        reqData.put(Constants.REQUEST_ATTR, requestAttr);

        return new HttpInputMessage() {
            @Override
            public InputStream getBody() throws IOException {
                return new ByteArrayInputStream(body);
            }
            @Override
            public HttpHeaders getHeaders() {
                return headers;
            }
        };
    }

    /**
     * 统一异常信息管理
     * */
    private MiayRuntimeException createCustomException(Exception e, ServiceContext serviceContext){
        MiayRuntimeException exception = null;
        if(e instanceof MiayRuntimeException){
            exception = (MiayRuntimeException)e;
        }else if(e instanceof InvocationTargetException){
            exception = new MiayRuntimeException(((InvocationTargetException) e).getTargetException());
        }else{
            exception = new MiayRuntimeException(e);
        }

        exception.setServiceContext(serviceContext);

        if(exception.getData()==null){
            Map<String,Object> errData = new HashMap<>();
            errData.put(Constants.RESPONSE_MSG_CONTENT, e.getMessage());
            exception.setData(errData);
        }
        return exception;
    }

    /**
     * 异常处理
     * */
    @ExceptionHandler(MiayRuntimeException.class)
    public ModelAndView exceptionHandler(MiayRuntimeException exception){
        logger.error(exception.getMessage(), exception);
        View view = null;
        try {
            view = viewResolver.resolveViewName(exception.getServiceContext().getContentType(), null);
        } catch (Exception e) {
            view = new DefaultView();
        }

        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setView(view);

        Map<String, Object> model = viewResolver.createModel(exception.getServiceContext().getContentType(), exception.getData(), exception.getErrorCode(), exception.getMessage(), null);
        modelAndView.addAllObjects(model);
        return modelAndView;
    }

    /**
     * 服务验证
     * */
    @RequestMapping("/hello")
    public String process(){
        return "Hello";
    }
}
